<?php

/**
 * 框架调配器
 */
class Dispatcher {

    /**
     * 请求对象
     *
     * @var object
     */
    private $_request;

    /**
     * 当前控制器名
     *
     * @var string
     */
    private $_controller;

    /**
     * 当前动作名
     *
     * @var string
     */
    private $_action;

    /**
     * 配置信息
     *
     * @var array
     */
    private $_appConfig = array();

    /**
     * 运行时信息
     *
     * @var array
     */
    private $_debugInfo = array();

    /**
     * 运行调度器
     */
    public function run() {

        // 读取系统配置文件
        $this->_appConfig = Sys::getCfg();

        // 记录运行开始时间
        $this->_setDebugInfo('beginTime', microtime(true));

        // 解析请求体，获取请求参数
        $this->_request = Request::getInstance();

        // 加载自定义路由
        if (file_exists(APP_PATH . '/route.php')) {
            require APP_PATH . '/route.php';
        }

        // 解析Url
        $this->_request->parseUri();

        // 获取当前模块
        define('MODULE_NAME', $this->_request->getModule());

        // 写入当前模块路径
        define('MODULE_PATH', APP_PATH . '/' . MODULE_NAME);

        // 记录模块名称
        $this->_setDebugInfo('module', MODULE_NAME);

        // 加载模块下的配置文件
        if (file_exists(MODULE_PATH . '/Config.php')) {
            $this->_appConfig = Sys::getCfg('', true);
        }

        // 运行模块下的 Bootstrap
        if (file_exists(MODULE_PATH . '/Bootstrap.php')) {
            require MODULE_PATH . '/Bootstrap.php';
            $bootStrap = new Bootstrap();
            if (method_exists($bootStrap, 'init')) {
                $bootStrap->init();
            }
        }
        
        // 得到控制器名和动作名
        $this->_controller = $this->_request->getController();
        $this->_action = $this->_request->getAction();

        // 实例化控制器
        $controller = $this->_getController();

        $action = $this->_action;

        // 记录动作名称
        $this->_setDebugInfo('action', $action);

        // 保留表单数据
        if ($this->_appConfig['form_cache']) {
            Http::setFormCache();
        }

        /**
         * 启用会话，延后到控制器实例化之后启动，以便可以在控制器构造函数中设置session_id或者设置是否禁用session
         * 注意：必须设置php.ini session.auto_start = 0，防止php自动开启会话，一般默认都是禁用状态
         */
        if ($this->_appConfig['session_auto_start']) {
            session_start(); // 启动session
        } else {
            session_abort(); // 及早结束session，防止session阻塞
        }

        // 启动输出缓存
        ob_start();

        // 非CLI运行模式要设置字符集，防止乱码
        if (PHP_SAPI != 'cli') {
            header("Content-Type:text/html; charset=" . $this->_appConfig['charset']);
        }

        // 执行 init 方法
        $controller->init();

        // 判断动作是否存在
        if (!method_exists($controller, $action)) {
            throw new Exception('控制器：' . get_class($controller) . ' 中未定义动作：' . $action);
        }

        // 执行动作
        $controller->$action();

        // 得到所有输出内容
        $viewContent = ob_get_clean();

        // 是否自动输出视图
        if ($controller->viewIsAutoRender()) {
            // 渲染视图文件
            $viewContent .= $controller->render($this->_action);

            // 记录视图名称
            $this->_setDebugInfo('view', $this->_action . '.html');
        }

        // 有视图内容则输出
        if ($viewContent != '') {
            echo $viewContent;
        }

        // 记录运行结束时间
        $this->_setDebugInfo('endTime', microtime(true));

        // 如果显示调试信息 并且 当前不是 AJAX 的请求 并且框架不是以 CLI方式运行
        if ($this->_appConfig['debug'] && !$this->_request->isAjax() && PHP_SAPI != 'cli') {
            $debugInfo = $this->_debugInfo;
            include CORE_PATH . '/Debug.php';
        }
    }

    /**
     * 得到当前的控件器
     */
    private function _getController() {

        // 得到控制器文件名
        $controllerFile = MODULE_PATH . '/Controller/' . $this->_controller . '.php';

        $controllerFile = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $controllerFile);

        if (!file_exists($controllerFile)) {
            throw new Exception("控制器不存在：$controllerFile");
        }

        // 包含控制器生成对象
        require $controllerFile;

        $className = MODULE_NAME . '\\Controller\\' . $this->_controller;

        // 类是否存在的
        if (!class_exists($className, false)) {
            throw new Exception("类：$className 未定义在：$controllerFile");
        }

        $controller = new $className();

        // 判断控制器是否继承基类
        if (!($controller instanceof Controller)) {
            throw new Exception("控制器类 $className 必需继承 Controller 基类");
        }

        // 记录控制器名称
        $this->_setDebugInfo('controller', $className);

        return $controller;
    }

    /**
     * 设置APP调试信息
     *
     * @param string $key 键
     * @param string $value 内容
     */
    private function _setDebugInfo($key, $value) {
        if ($this->_appConfig['debug']) {
            $this->_debugInfo[$key] = $value;
        }
    }

}
